package main

import (
	"container/list"
	"fmt"
	"github.com/antlr/antlr4/runtime/Go/antlr/v4"
	SA "sago/ast"
	"strconv"
	"strings"
)

type Group struct {
	index int
	gnum  int
	text  string
}

type SAReader struct {
	SA.BaseSAListener
	indent   int
	stack    list.List
	group    list.List
	fileName string

	doc *DatasetDocument
}

func NewSAReader() *SAReader {
	return &SAReader{
		indent: 0,
	}
}

//func (s *SAReader) top() interface{} {
//    ASSERT(s.stack.Len() > 0)
//    return s.stack.Back()
//}

//func (s *SAReader) push(v interface{}) {
//    ASSERT(v != nil)
//    s.stack.PushBack(v)
//}

//func (s *SAReader) pop() interface{} {
//    ASSERT(s.stack.Len() > 0)
//    return s.stack.Remove(s.stack.Back())
//}

func (s *SAReader) prefix() string {
	return strings.Repeat("    ", s.indent)
}

func (s *SAReader) Read(filename string) interface{} {
	s.fileName = filename
	input, _ := antlr.NewFileStream(filename)
	lexer := SA.NewSALexer(input)
	tokenStream := antlr.NewCommonTokenStream(lexer, 0)
	p := SA.NewSAParser(tokenStream)
	//p.AddParseListener(s)
	tree := p.Sa_file()
	antlr.ParseTreeWalkerDefault.Walk(s, tree)
	return s.doc
}

// VisitTerminal is called when a terminal node is visited.
func (s *SAReader) VisitTerminal(node antlr.TerminalNode) {
	//println(s.prefix(0), "VisitTerminal")
}

// VisitErrorNode is called when an error node is visited.
func (s *SAReader) VisitErrorNode(node antlr.ErrorNode) {
	//println(s.prefix(0), "VisitErrorNode")
}

// EnterEveryRule is called when any rule is entered.
func (s *SAReader) EnterEveryRule(ctx antlr.ParserRuleContext) {
	//fmt.Printf("%v EnterEveryRule\n", s.prefix())
	s.indent++
}

// ExitEveryRule is called when any rule is exited.
func (s *SAReader) ExitEveryRule(ctx antlr.ParserRuleContext) {
	//fmt.Printf("%v LeaveEveryRule\n", s.prefix())
	s.indent--
}

// EnterSa_file is called when production sa_file is entered.
func (s *SAReader) EnterSa_file(ctx *SA.Sa_fileContext) {
	s.doc = NewDatasetDocument(s.fileName)
	fmt.Printf("%v EnterSa_file\n", s.prefix())
}

// ExitSa_file is called when production sa_file is exited.
func (s *SAReader) ExitSa_file(ctx *SA.Sa_fileContext) {
	fmt.Printf("%v LeaveSa_file\n", s.prefix())
}

// EnterHeader_section is called when production header_section is entered.
func (s *SAReader) EnterHeader_section(ctx *SA.Header_sectionContext) {
	s.group.PushBack(Group{index: s.stack.Len(), text: ctx.GetText()})
	fmt.Printf("%v EnterHeader_section\n", s.prefix())
}

// ExitHeader_section is called when production header_section is exited.
func (s *SAReader) ExitHeader_section(ctx *SA.Header_sectionContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	for index := 0; index < size; index++ {
		property := s.stack.Remove(s.stack.Back()).(*Property)
		s.doc.HeaderItems.PushBack(property)
	}

	fmt.Printf("%v EnterHeader_section\n", s.prefix())
}

// EnterType_section is called when production type_section is entered.
func (s *SAReader) EnterType_section(ctx *SA.Type_sectionContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterType_section\n", s.prefix())
}

// ExitType_section is called when production type_section is exited.
func (s *SAReader) ExitType_section(ctx *SA.Type_sectionContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	for index := 0; index < size; index++ {
		valueTypeDefinition := s.stack.Remove(s.stack.Back()).(*TypeDefinition)
		s.doc.TypeItems.PushBack(valueTypeDefinition)
	}

	fmt.Printf("%v LeaveType_section\n", s.prefix())
}

// EnterSweep_section is called when production sweep_section is entered.
func (s *SAReader) EnterSweep_section(ctx *SA.Sweep_sectionContext) {
	fmt.Printf("%v EnterSweep_section\n", s.prefix())
}

// ExitSweep_section is called when production sweep_section is exited.
func (s *SAReader) ExitSweep_section(ctx *SA.Sweep_sectionContext) {
	fmt.Printf("%v LeaveSweep_section\n", s.prefix())
}

// EnterTrace_section is called when production trace_section is entered.
func (s *SAReader) EnterTrace_section(ctx *SA.Trace_sectionContext) {
	// 增加一个没啥用的 Group，以支持 group 的 trace 在获取父 Group 时，一定是在 trace 范围内
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterTrace_section\n", s.prefix())
}

// ExitTrace_section is called when production trace_section is exited.
func (s *SAReader) ExitTrace_section(ctx *SA.Trace_sectionContext) {
	s.group.Remove(s.group.Back())
	fmt.Printf("%v LeaveTrace_section\n", s.prefix())
}

// EnterValue_section is called when production value_section is entered.
func (s *SAReader) EnterValue_section(ctx *SA.Value_sectionContext) {
	fmt.Printf("%v EnterValue_section\n", s.prefix())
}

// ExitValue_section is called when production value_section is exited.
func (s *SAReader) ExitValue_section(ctx *SA.Value_sectionContext) {
	fmt.Printf("%v LeaveValue_section\n", s.prefix())
}

// EnterHeader_item is called when production header_item is entered.
func (s *SAReader) EnterHeader_item(ctx *SA.Header_itemContext) {
	s.group.PushBack(Group{index: s.stack.Len(), text: ctx.GetText()})
	fmt.Printf("%v EnterHeader_item\n", s.prefix())
}

// ExitHeader_item is called when production header_item is exited.
func (s *SAReader) ExitHeader_item(ctx *SA.Header_itemContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index
	ASSERT(size == 2)

	value := s.stack.Remove(s.stack.Back()).(Value)
	name := s.stack.Remove(s.stack.Back()).(string)

	s.stack.PushBack(&Property{Name: name, Value: value})

	fmt.Printf("%v LeaveHeader_item\n", s.prefix())
}

// EnterType_item is called when production type_item is entered.
func (s *SAReader) EnterType_item(ctx *SA.Type_itemContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterType_item\n", s.prefix())
}

// ExitType_item is called when production type_item is exited.
func (s *SAReader) ExitType_item(ctx *SA.Type_itemContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	var props *Properties = nil
	if size == 3 {
		last := s.stack.Back()
		props = s.stack.Remove(last).(*Properties)
	}

	valueTypeDefinition := s.stack.Remove(s.stack.Back()).(*TypeDefinition)
	valueTypeName := s.stack.Remove(s.stack.Back()).(string)

	// 保存起来
	valueTypeDefinition.Name = valueTypeName
	if props != nil {
		valueTypeDefinition.Prop = props
	}

	s.stack.PushBack(valueTypeDefinition)

	fmt.Printf("%v LeaveType_item\n", s.prefix())
}

// EnterSweep_item is called when production sweep_item is entered.
func (s *SAReader) EnterSweep_item(ctx *SA.Sweep_itemContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterSweep_item\n", s.prefix())
}

// ExitSweep_item is called when production sweep_item is exited.
func (s *SAReader) ExitSweep_item(ctx *SA.Sweep_itemContext) {
	group := s.group.Remove(s.group.Back()).(Group)

	size := s.stack.Len() - group.index

	var props *Properties = nil
	if size == 3 {
		last := s.stack.Back()
		props = s.stack.Remove(last).(*Properties)
	}

	typeName := s.stack.Remove(s.stack.Back()).(string)
	signalName := s.stack.Remove(s.stack.Back()).(string)

	// 保存起来
	signalDefinition := &SignalDefinition{
		Kind:           SignalKindSweep,
		SignalName:     signalName,
		SignalTypeName: typeName,
		SignalType:     nil,
		Props:          props,
	}

	s.doc.SignalItems.PushBack(signalDefinition)

	s.doc.SequenceItems.PushBack(&ValueSequence{
		SignalName:       signalDefinition.SignalName,
		SignalDefinition: signalDefinition,
		SignalValues:     list.New(),
		Prop:             make(map[int]*Properties),
	})

	fmt.Printf("%v LeaveSweep_item\n", s.prefix())
}

// EnterTrace_item_normal is called when production trace_item_normal is entered.
func (s *SAReader) EnterTrace_item_normal(ctx *SA.Trace_item_normalContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterTrace_item_normal\n", s.prefix())
}

// ExitTrace_item_normal is called when production trace_item_normal is exited.
func (s *SAReader) ExitTrace_item_normal(ctx *SA.Trace_item_normalContext) {
	fmt.Printf("%v LeaveTrace_item_normal\n", s.prefix())

	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	var props *Properties = nil
	if size == 3 {
		last := s.stack.Back()
		props = s.stack.Remove(last).(*Properties)
	}

	typeName := s.stack.Remove(s.stack.Back()).(string)
	signalName := s.stack.Remove(s.stack.Back()).(string)

	// 保存起来
	signalDefinition := &SignalDefinition{
		Kind:           SignalKindTrace,
		SignalName:     signalName,
		SignalTypeName: typeName,
		SignalType:     nil,
		Props:          props,
	}

	parentGroup := s.group.Back().Value.(Group)
	if parentGroup.gnum > 0 {
		groupSignalDefinition := s.stack.Back().Value.(*SignalDefinition)
		groupSignalDefinition.Children.PushBack(signalDefinition)

		// 如果数据还没累积足够
		if groupSignalDefinition.Children.Len() < parentGroup.gnum {
			return
		}

		// 将这个 group signal 弹出来
		s.stack.Remove(s.stack.Back())

		// 替换信号，方便后面保存
		signalDefinition = groupSignalDefinition
	}

	s.doc.SignalItems.PushBack(signalDefinition)

	s.doc.SequenceItems.PushBack(&ValueSequence{
		SignalName:       signalDefinition.SignalName,
		SignalDefinition: signalDefinition,
		SignalValues:     list.New(),
		Prop:             make(map[int]*Properties),
	})
}

// EnterTrace_item_group is called when production trace_item_group is entered.
func (s *SAReader) EnterTrace_item_group(ctx *SA.Trace_item_groupContext) {
	groupChildCount, _ := strconv.ParseInt(ctx.LITERAL_INTEGER().GetText(), 0, 32)
	s.group.PushBack(Group{index: s.stack.Len(), gnum: int(groupChildCount)})

	signalName := ctx.LITERAL_STRING().GetText()

	// 保存起来
	signalDefinition := &SignalDefinition{
		Kind:           SignalKindTrace,
		SignalName:     signalName,
		SignalTypeName: "",
		Children:       NewSignalDefinitions(),
		SignalType:     nil,
		Props:          nil,
	}

	// trace_item_group 对应的 signal 需要提前保存
	s.stack.PushBack(signalDefinition)

	fmt.Printf("%v EnterTrace_item_group\n", s.prefix())
}

// ExitTrace_item_group is called when production trace_item_group is exited.
func (s *SAReader) ExitTrace_item_group(ctx *SA.Trace_item_groupContext) {
	fmt.Printf("%v LeaveTrace_item_group\n", s.prefix())
}

// EnterValue_item_group is called when production value_item_group is entered.
func (s *SAReader) EnterValue_item_group(ctx *SA.Value_item_groupContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterValue_item_group\n", s.prefix())
}

// ExitValue_item_group is called when production value_item_group is exited.
func (s *SAReader) ExitValue_item_group(ctx *SA.Value_item_groupContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index
	ASSERT(size >= 2)

	valueCount := size

	var props *Properties = nil
	if ctx.Prop() != nil {
		props = s.stack.Remove(s.stack.Back()).(*Properties)
		valueCount-- // 去掉 prop
	}

	valueCount-- // 去掉 name

	// 提取栈上的数据组成数组
	valueArray := make([]Value, valueCount, valueCount)
	for index := 0; index < (size - 1); index++ {
		valueArray[index] = s.stack.Remove(s.stack.Back()).(Value)
	}

	// 信号名称
	signalName := s.stack.Remove(s.stack.Back()).(string)

	// 找到信号的值序列
	valueSequence, ok := s.doc.FindSequence(signalName)
	ASSERT(ok)

	// 保存信号值组
	valueSequence.SignalValues.PushBack(valueArray)

	if props != nil {
		valueSequence.Prop[valueSequence.SignalValues.Len()] = props
	}

	fmt.Printf("%v LeaveValue_item_group\n", s.prefix())
}

// EnterValue_item_ref is called when production value_item_ref is entered.
func (s *SAReader) EnterValue_item_ref(ctx *SA.Value_item_refContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterValue_item_ref\n", s.prefix())
}

// ExitValue_item_ref is called when production value_item_ref is exited.
func (s *SAReader) ExitValue_item_ref(ctx *SA.Value_item_refContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	// 从栈上提取数据
	var props *Properties = nil
	if size == 4 {
		props = s.stack.Remove(s.stack.Back()).(*Properties)
	}

	signalValue := s.stack.Remove(s.stack.Back()).(Value)
	signalTypeName := s.stack.Remove(s.stack.Back()).(string)
	signalName := s.stack.Remove(s.stack.Back()).(string)

	// 找到类型定义
	typeDefinition, ok := s.doc.FindType(signalTypeName)
	ASSERT(ok)

	// 找到信号定义
	signalDefinition, ok := s.doc.FindSignal(signalName)
	if !ok {
		signalDefinition = &SignalDefinition{
			Kind:           SignalKindTrace,
			SignalName:     signalName,
			SignalTypeName: signalTypeName,
			SignalType:     typeDefinition,
			Props:          nil,
		}

		s.doc.SignalItems.PushBack(signalDefinition)

		sequence := &ValueSequence{
			SignalName:       signalName,                //       string              // 名称
			SignalDefinition: signalDefinition,          // *SignalDefinition   // 信号定义
			SignalValues:     list.New(),                //     *list.List          // Value
			Prop:             make(map[int]*Properties), //             map[int]*Properties // 属性 *Property
		}

		s.doc.SequenceItems.PushBack(sequence)
	}

	// 将信号的值保存到信号序列
	valueSequence, ok := s.doc.FindSequence(signalName)
	ASSERT(ok)

	// 保存值
	valueSequence.SignalValues.PushBack(signalValue)

	// 如果有属性，就将属性单独保存起来
	if props != nil {
		valueSequence.Prop[valueSequence.SignalValues.Len()] = props
	}

	fmt.Printf("%v LeaveValue_item_ref\n", s.prefix())
}

// EnterValue_item_normal is called when production value_item_normal is entered.
func (s *SAReader) EnterValue_item_normal(ctx *SA.Value_item_normalContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterValue_item_normal\n", s.prefix())
}

// ExitValue_item_normal is called when production value_item_normal is exited.
func (s *SAReader) ExitValue_item_normal(ctx *SA.Value_item_normalContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	var props *Properties = nil
	if size == 3 {
		props = s.stack.Remove(s.stack.Back()).(*Properties)
	}

	signalValue := s.stack.Remove(s.stack.Back()).(Value)
	signalName := s.stack.Remove(s.stack.Back()).(string)

	valueSequence, ok := s.doc.FindSequence(signalName)
	ASSERT(ok)

	valueSequence.SignalValues.PushBack(signalValue)
	if props != nil {
		valueSequence.Prop[valueSequence.SignalValues.Len()] = props
	}

	fmt.Printf("%v LeaveValue_item_normal\n", s.prefix())
}

// EnterType_int is called when production type_int is entered.
func (s *SAReader) EnterType_int(ctx *SA.Type_intContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterType_int\n", s.prefix())
}

// ExitType_int is called when production type_int is exited.
func (s *SAReader) ExitType_int(ctx *SA.Type_intContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	ASSERT((s.stack.Len() - group.index) == 1)

	kindRefined := s.stack.Remove(s.stack.Back()).(uint8)

	valueTypeDefinition := &TypeDefinition{
		Kind:     KindClassifyInt | kindRefined,
		Name:     "",
		ElemType: nil,
		Members:  nil,
		Prop:     nil,
	}

	s.stack.PushBack(valueTypeDefinition)

	fmt.Printf("%v LeaveType_int\n", s.prefix())
}

// EnterType_float is called when production type_float is entered.
func (s *SAReader) EnterType_float(ctx *SA.Type_floatContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterType_float\n", s.prefix())
}

// ExitType_float is called when production type_float is exited.
func (s *SAReader) ExitType_float(ctx *SA.Type_floatContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	ASSERT((s.stack.Len() - group.index) == 1)

	kindRefined := s.stack.Remove(s.stack.Back()).(uint8)
	valueTypeDefinition := &TypeDefinition{
		Kind:     KindClassifyFloat | kindRefined,
		Name:     "",
		ElemType: nil,
		Members:  nil,
		Prop:     nil,
	}

	s.stack.PushBack(valueTypeDefinition)
	fmt.Printf("%v LeaveType_float\n", s.prefix())
}

// EnterType_complex is called when production type_complex is entered.
func (s *SAReader) EnterType_complex(ctx *SA.Type_complexContext) {
	s.group.PushBack(Group{index: s.stack.Len()})

	fmt.Printf("%v EnterType_complex\n", s.prefix())
}

// ExitType_complex is called when production type_complex is exited.
func (s *SAReader) ExitType_complex(ctx *SA.Type_complexContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	ASSERT((s.stack.Len() - group.index) == 1)

	kindRefined := s.stack.Remove(s.stack.Back()).(uint8)
	valueTypeDefinition := &TypeDefinition{
		Kind:     KindClassifyComplex | kindRefined,
		Name:     "",
		ElemType: nil,
		Members:  nil,
		Prop:     nil,
	}

	s.stack.PushBack(valueTypeDefinition)

	fmt.Printf("%v LeaveType_complex\n", s.prefix())
}

// EnterType_string is called when production type_string is entered.
func (s *SAReader) EnterType_string(ctx *SA.Type_stringContext) {
	valueTypeDefinition := &TypeDefinition{
		Kind:     KindClassifyString | KindRefinedPointer,
		Name:     "",
		ElemType: nil,
		Members:  nil,
		Prop:     nil,
	}

	s.stack.PushBack(valueTypeDefinition)

	fmt.Printf("%v EnterType_string\n", s.prefix())
}

// ExitType_string is called when production type_string is exited.
func (s *SAReader) ExitType_string(ctx *SA.Type_stringContext) {
	fmt.Printf("%v LeaveType_string\n", s.prefix())
}

// EnterType_array is called when production type_array is entered.
func (s *SAReader) EnterType_array(ctx *SA.Type_arrayContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterType_array\n", s.prefix())
}

// ExitType_array is called when production type_array is exited.
func (s *SAReader) ExitType_array(ctx *SA.Type_arrayContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index
	ASSERT(size == 1)

	elemType := s.stack.Remove(s.stack.Back()).(*TypeDefinition)
	valueTypeDefinition := &TypeDefinition{
		Kind:     KindClassifyArray | KindRefinedPointer,
		Name:     "",
		ElemType: elemType,
		Members:  nil,
		Prop:     nil,
	}

	s.stack.PushBack(valueTypeDefinition)
	fmt.Printf("%v LeaveType_array\n", s.prefix())
}

// EnterType_struct is called when production type_struct is entered.
func (s *SAReader) EnterType_struct(ctx *SA.Type_structContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterType_struct\n", s.prefix())
}

// ExitType_struct is called when production type_struct is exited.
func (s *SAReader) ExitType_struct(ctx *SA.Type_structContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	valueTypeDefinition := &TypeDefinition{
		Kind:     KindClassifyStruct | KindRefinedPointer,
		Name:     "",
		ElemType: nil,
		Members:  NewMemberDefinitions(),
		Prop:     nil,
	}

	for index := 0; index < size; index++ {
		back := s.stack.Back()
		member := s.stack.Remove(back).(*MemberDefinition)
		valueTypeDefinition.Members.PushBack(member)
	}

	s.stack.PushBack(valueTypeDefinition)
	fmt.Printf("%v LeaveType_struct\n", s.prefix())
}

// EnterKind_refined is called when production kind_refined is entered.
func (s *SAReader) EnterKind_refined(ctx *SA.Kind_refinedContext) {
	if ctx.BYTE() != nil {
		s.stack.PushBack(KindRefinedByte)
	} else if ctx.SHORT() != nil {
		s.stack.PushBack(KindRefinedShort)
	} else if ctx.LONG() != nil {
		s.stack.PushBack(KindRefinedLong)
	} else if ctx.DOUBLE() != nil {
		s.stack.PushBack(KindRefinedDouble)
	} else {
		ASSERT(false)
	}

	fmt.Printf("%v EnterKind_refined\n", s.prefix())
}

// ExitKind_refined is called when production kind_refined is exited.
func (s *SAReader) ExitKind_refined(ctx *SA.Kind_refinedContext) {
	fmt.Printf("%v LeaveKind_refined\n", s.prefix())
}

// EnterMember is called when production member is entered.
func (s *SAReader) EnterMember(ctx *SA.MemberContext) {
	s.group.PushBack(Group{index: s.stack.Len()})
	fmt.Printf("%v EnterMember\n", s.prefix())
}

// ExitMember is called when production member is exited.
func (s *SAReader) ExitMember(ctx *SA.MemberContext) {
	group := s.group.Remove(s.group.Back()).(Group)
	size := s.stack.Len() - group.index

	var props *Properties = nil
	if size == 3 {
		props = s.stack.Remove(s.stack.Back()).(*Properties)
	}

	typeDefintion := s.stack.Remove(s.stack.Back()).(*TypeDefinition)
	name := s.stack.Remove(s.stack.Back()).(string)

	member := &MemberDefinition{
		Name: name,          // string
		Type: typeDefintion, // *TypeDefinition
		Prop: props,         // *Properties
	}

	s.stack.PushBack(member)

	fmt.Printf("%v LeaveMember\n", s.prefix())
}

// EnterProp is called when production prop is entered.
func (s *SAReader) EnterProp(ctx *SA.PropContext) {
	s.group.PushBack(Group{index: s.stack.Len(), text: ctx.GetText()})

	fmt.Printf("%v EnterProp\n", s.prefix())
}

// ExitProp is called when production prop is exited.
func (s *SAReader) ExitProp(ctx *SA.PropContext) {
	group := s.group.Remove(s.group.Back()).(Group)

	size := s.stack.Len() - group.index
	ASSERT((size % 2) == 0)

	props := NewProperties()

	for i := 0; i < (size / 2); i++ {
		value := s.stack.Remove(s.stack.Back()).(Value)
		name := s.stack.Remove(s.stack.Back()).(string)
		props.PushBack(&Property{
			Name:  name,
			Value: value,
		})
	}

	s.stack.PushBack(props)

	fmt.Printf("%v LeaveProp\n", s.prefix())
}

// EnterReftype is called when production reftype is entered.
func (s *SAReader) EnterReftype(ctx *SA.ReftypeContext) {
	s.stack.PushBack(ctx.LITERAL_STRING().GetText())
	fmt.Printf("%v EnterReftype\n", s.prefix())
}

// ExitReftype is called when production reftype is exited.
func (s *SAReader) ExitReftype(ctx *SA.ReftypeContext) {
	fmt.Printf("%v LeaveReftype\n", s.prefix())
}

// EnterName is called when production name is entered.
func (s *SAReader) EnterName(ctx *SA.NameContext) {
	s.stack.PushBack(ctx.GetText())
	fmt.Printf("%v EnterName: %s\n", s.prefix(), ctx.GetText())
}

// ExitName is called when production name is exited.
func (s *SAReader) ExitName(ctx *SA.NameContext) {
	fmt.Printf("%v LeaveName\n", s.prefix())
}

// EnterValue_bool is called when production value_bool is entered.
func (s *SAReader) EnterValue_bool(ctx *SA.Value_boolContext) {
	value, _ := strconv.ParseBool(ctx.GetText())
	s.stack.PushBack(value)
	fmt.Printf("%v EnterValue_bool: %v\n", s.prefix(), FormatValue(value))
}

// ExitValue_bool is called when production value_bool is exited.
func (s *SAReader) ExitValue_bool(ctx *SA.Value_boolContext) {
	fmt.Printf("%v LeaveValue_bool\n", s.prefix())
}

// EnterValue_string is called when production value_string is entered.
func (s *SAReader) EnterValue_string(ctx *SA.Value_stringContext) {
	value := ctx.GetText()
	s.stack.PushBack(value)
	fmt.Printf("%v EnterValue_string: %v\n", s.prefix(), FormatValue(value))
}

// ExitValue_string is called when production value_string is exited.
func (s *SAReader) ExitValue_string(ctx *SA.Value_stringContext) {
	fmt.Printf("%v LeaveValue_string\n", s.prefix())
}

// EnterValue_integer is called when production value_integer is entered.
func (s *SAReader) EnterValue_integer(ctx *SA.Value_integerContext) {
	value, _ := strconv.ParseInt(ctx.GetText(), 0, 32)
	s.stack.PushBack(value)
	fmt.Printf("%v EnterValue_integer: %v\n", s.prefix(), FormatValue(value))
}

// ExitValue_integer is called when production value_integer is exited.
func (s *SAReader) ExitValue_integer(ctx *SA.Value_integerContext) {
	fmt.Printf("%v LeaveValue_integer\n", s.prefix())
}

// EnterValue_float is called when production value_float is entered.
func (s *SAReader) EnterValue_float(ctx *SA.Value_floatContext) {
	value, _ := strconv.ParseFloat(ctx.GetText(), 64)
	s.stack.PushBack(value)
	fmt.Printf("%v EnterValue_real: %v\n", s.prefix(), FormatValue(value))
}

// ExitValue_float is called when production value_float is exited.
func (s *SAReader) ExitValue_float(ctx *SA.Value_floatContext) {
	fmt.Printf("%v LeaveValue_float\n", s.prefix())
}

// EnterValue_group is called when production value_group is entered.
func (s *SAReader) EnterValue_group(ctx *SA.Value_groupContext) {
	s.group.PushBack(Group{index: s.stack.Len(), text: ctx.GetText()})
}

// ExitValue_group is called when production value_group is exited.
func (s *SAReader) ExitValue_group(ctx *SA.Value_groupContext) {
	group := s.group.Remove(s.group.Back()).(Group)

	size := s.stack.Len() - group.index
	groupValues := make([]interface{}, size, size)

	for index := 0; index < size; index++ {
		groupValues[index] = s.stack.Remove(s.stack.Back()).(Value)
	}

	s.stack.PushBack(groupValues)
}
